package jp.ac.u_tokyo.dch.util;

import java.io.File;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.apache.commons.lang.xwork.StringUtils;
import org.apache.log4j.Logger;
import org.simpleframework.xml.Serializer;
import org.simpleframework.xml.core.Persister;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
/**
* @version 1.1
*/
public class XmlCommonUtil {

	/** ロガー */
	private static Logger logger = Logger.getLogger(XmlCommonUtil.class);

	/**
	 * xmlからオブジェクトに変換する
	 *  区切るタグから
	 * @param <T> オブジェクト型
	 * @param xml 文字列
	 * @param tagDelim 区切るタグ
	 * @param type クラスタイプ
	 * @return
	 */
	public static <T> T xmlToObject(String xml, String tagDelim,
			Class<? extends T> type) {

		Document objDoc = xmlToDom(xml);

		NodeList list = objDoc.getElementsByTagName(tagDelim);

		if (list.getLength() == 0) {
			return null;
		}

		return xmlToObject(xmlToString(list.item(0)), type);
	}

	/**
	 * xmlからオブジェクトに変換する
	 * @param <T> オブジェクト型
	 * @param xml 文字列
	 * @param type クラスタイプ
	 * @return オブジェクト型
	 */
	public static <T> T xmlToObject(String xml, Class<? extends T> type) {

		Serializer serializer = new Persister();

		T obj = null;
		try {
			obj = serializer.read(type, xml);
		} catch (Exception e) {
			logger.error(e);
		}

		return obj;
	}

	/**
	 * オブジェクトからXML文字列に変換
	 * @param source オブジェクト
	 * @return XML文字列
	 * @throws Exception 例外
	 */
	public static String ObjectToXml(Object source) throws Exception {

		Serializer serializer = new Persister();
		StringWriter objsr = new StringWriter();
		serializer.write(source, objsr);
		return objsr.toString();
	}

	/**
	 *オブジェクトリスト
	 * @param <T> オブジェクト
	 * @param xml XML文字列
	 * @param tagDelim 区切るタグ
	 * @param type クラスタイプ
	 * @return オブジェクトリスト
	 */
	public static <T> List<T> xmlToList(String xml, String tagDelim,
			Class<? extends T> type) {

		List<T> resultList = new ArrayList<T>();
		Document objDoc = xmlToDom(xml);
		NodeList list = objDoc.getElementsByTagName(tagDelim);
		for (int i = 0; i < list.getLength(); i++) {
			Node node = list.item(i);
			resultList.add(xmlToObject(xmlToString(node), type));
		}

		return resultList;
	}

	/**
	 * 結果を返す パーさに要らない情報を削除する
	 * @param xml
	 * @return XML文字列
	 */
	public static String getXmlContents(String xml) {

		xml = xml.replaceAll("<\\?xml.+?>", "");
		xml = xml.replaceAll("<Query-Results>", "");
		xml = xml.replaceAll("</Query-Results>", "");
		xml = xml.replaceAll("\n", "");
		xml = xml.replaceAll("\t", "");
		return xml;
	}

	/**
	 * 数字結果を返す
	 *
	 * @param xml 文字列
	 * @return 数字
	 */
	public static Integer getSingleResult(String xml) {

		xml = getXmlContents(xml);
		xml = StringUtils.deleteWhitespace(xml);
		if (xml.length() == 0) {
			return 0;
		}

		return Integer.valueOf(xml);

	}

	/**
	 *
	 * @param <T> クラス型
	 * @param file ファイル
	 * @param type クラスタイプ
	 * @return
	 */
	public static <T> T xmlFileToObject(File file, Class<? extends T> type) {

		Serializer serializer = new Persister();

		T obj = null;
		try {
			logger.info(file.getAbsolutePath());
			obj = serializer.read(type, file);
			logger.info("変換正常終了");
		} catch (Exception e) {
			logger.error(e);
		}
		return obj;

	}

	/**
	 * NODEをXML文字列に変換する
	 * @param node
	 * @return XML文字列
	 */
	public static String xmlToString(Node node) {
		try {
			Source source = new DOMSource(node);
			StringWriter stringWriter = new StringWriter();
			Result result = new StreamResult(stringWriter);
			TransformerFactory factory = TransformerFactory.newInstance();
			Transformer transformer = factory.newTransformer();
			transformer.transform(source, result);
			return stringWriter.getBuffer().toString();
		} catch (TransformerConfigurationException e) {
		} catch (TransformerException e) {
		}
		return null;
	}

	/**
	 * xml文字列をDomオブジェクトに変換する
	 * @param xml
	 * @return Domオブジェクト
	 */
	private static Document xmlToDom(String xml) {

		StringReader objsr = null;
		DocumentBuilderFactory objDbf = null;
		DocumentBuilder objDb = null;
		Document objDoc = null;
		try {
			objsr = new StringReader(xml);
			objDbf = DocumentBuilderFactory.newInstance();
			objDb = objDbf.newDocumentBuilder();
			objDoc = objDb.parse(new InputSource(objsr));

		} catch (Exception e) {
			logger.error(e);
		} finally {
			if (objsr != null) {
				objsr.close();
			}
			objDb = null;
			objDbf = null;
		}

		return objDoc;
	}
}
